#include "Precomputation.h"
#include "Sort.h"

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#define TESTONE 0
#define TESTTWO 0
#define CREATE_MATLAB_COMMANDS 1

#define MATLAB_USE_HEURISTIC 0

typedef std::vector<std::vector<unsigned int> > Permutations;
Permutations previousPermutations;

void createDataset(const char* p_filename, unsigned int p_attributeCount, unsigned int*& p_intersections, Strings& p_attributes)
{
	Precomputation::calculateIntersections(p_filename, p_intersections, p_attributes);

	// decrease size of the dataset
	if (p_attributes.size() > p_attributeCount)
	{
		unsigned int* newRealDataIntersections = new unsigned int[p_attributeCount * p_attributeCount];
		for (unsigned int i = 0; i < p_attributeCount; ++i)
		{
			for (unsigned int j = 0; j < p_attributeCount; ++j)
			{
				newRealDataIntersections[i * p_attributeCount + j] = p_intersections[i * p_attributes.size() + j];
			}
		}
		delete[] p_intersections;
		p_intersections = newRealDataIntersections;
		p_attributes.erase(p_attributes.begin() + p_attributeCount, p_attributes.end());
	}
}

void getRandomSubset(unsigned int p_setSize, unsigned int p_subsetSize, unsigned int* p_intersections, const Strings& p_attributes, unsigned int*& p_newIntersections, Strings& p_newAttributes)
{
	std::vector<unsigned int> indexes;
	for (unsigned int i = 0; i < p_setSize; ++i)
	{
		indexes.push_back(i);
	}

	std::vector<unsigned int> newIndexes;

	for (unsigned int i = 0; i < p_subsetSize; ++i)
	{
		unsigned int index = rand() % indexes.size();
		newIndexes.push_back(indexes[index]);
		indexes.erase(indexes.begin() + index);
	}

	std::sort(newIndexes.begin(), newIndexes.end());

	for (Permutations::iterator it = previousPermutations.begin(); it != previousPermutations.end(); ++it)
	{
		if (std::equal(newIndexes.begin(), newIndexes.end(), (*it).begin()))
		{
			// Subset allready used.
			return;
		}
	}

	previousPermutations.push_back(newIndexes);
	p_newIntersections = new unsigned int[p_subsetSize * p_subsetSize];

	// Get the attributes for the subset
	for (std::vector<unsigned int>::iterator it = newIndexes.begin(); it != newIndexes.end(); ++it)
	{
		p_newAttributes.push_back(p_attributes[(*it)]);
	}

	// Get the intersections for the subset
	for (unsigned int i = 0; i < p_subsetSize; ++i)
	{
		for (unsigned int j = 0; j < p_subsetSize; ++j)
		{
			p_newIntersections[i * p_subsetSize + j] = p_intersections[newIndexes[i] * p_setSize + newIndexes[j]];
		}
	}
}

void runTestOne(std::ofstream& p_outputFile)
{
	std::cout << "Running test 1 ";

	for (unsigned int i = 20; i <= 20; ++i) // 16-20
	{
		p_outputFile << "n = " << i << std::endl;

		unsigned int* intersections;
		Strings attributes;

		createDataset("real data.txt", i, intersections, attributes);

		int* attributeOrder1 = 0;
		unsigned int bruteForceDynamicIntersectionCount = 0;
		float time1;
		
		Sort::bruteForceSortDynamic(intersections, attributes.size(), bruteForceDynamicIntersectionCount, attributeOrder1, time1);

		p_outputFile << "Algorithm 1: " << "-" << std::endl;
		p_outputFile << "Algorithm 2: " << time1 << " seconds" << std::endl << std::endl;

		delete[] attributeOrder1;
		delete[] intersections;

		std::cout << ".";
	}

	std::cout << std::endl << "Test 1 complete." << std::endl << std::endl;
}

void runTestTwo(std::ofstream& p_outputFile)
{
	p_outputFile << "***** Test 2 *****" << std::endl << std::endl;
	std::cout << "Running test 2 ";

	unsigned int* intersections;
	Strings attributes;

	createDataset("real data.txt", 100, intersections, attributes);

	p_outputFile << "Real data." << std::endl;

	for (unsigned int i = 10; i <= 14; ++i) // 10 - 20
	{
		p_outputFile << "Set size: " << i << std::endl;
		unsigned int numberOfTests = 100;
		while(previousPermutations.size() < numberOfTests) // 100
		{
			unsigned int* newIntersections = 0;
			Strings newAttributes;
			getRandomSubset(attributes.size(), i, intersections, attributes, newIntersections, newAttributes);

			int* attributeOrder1 = 0;
			int* attributeOrder2 = 0;
			unsigned int bruteForceDynamicIntersectionCount = 0;
			unsigned int heuristicIntersectionCount = 0;
			float time1;
			float time2;

			Sort::bruteForceSortDynamic(newIntersections, newAttributes.size(), bruteForceDynamicIntersectionCount, attributeOrder1, time1);
			Sort::heuristicSort(newIntersections, newAttributes.size(), heuristicIntersectionCount, attributeOrder2, time2);

			p_outputFile << bruteForceDynamicIntersectionCount << "\t" << heuristicIntersectionCount << "\t" << time1 << "\t" << time2 << "\t";
			for (unsigned int j = 0; j < newAttributes.size(); ++j)
			{
				p_outputFile << attributeOrder1[j] << " ";
			}
			p_outputFile << "\t";
			for (unsigned int j = 0; j < newAttributes.size(); ++j)
			{
				p_outputFile << attributeOrder2[j] << " ";
			}
			p_outputFile << "\t";
			for (unsigned int j = 0; j < newAttributes.size(); ++j)
			{
				p_outputFile << newAttributes[j] << " ";
			}
			p_outputFile << "\n";

			delete[] attributeOrder1;
			delete[] attributeOrder2;

			delete[] newIntersections;
		}

		p_outputFile << std::endl;

		std::cout << ".";

		previousPermutations.clear();
	}

	std::cout << std::endl << "Test 2 complete." << std::endl << std::endl;

	delete[] intersections;
}

void createMatlabCommands()
{
	std::cout << "Creating matlab commands." << std::endl;

	Precomputation::createMatlabCommands("real data.txt", MATLAB_USE_HEURISTIC);

	std::cout << "Done." << std::endl;
}

void main()
{
	std::ofstream outputFile("Test Results.txt");
	//outputFile.precision(100);
	outputFile << "***** Test 1 *****" << std::endl << std::endl;

#if TESTONE
	runTestOne(outputFile);
#endif

#if TESTTWO
	runTestTwo(outputFile);
#endif

#if CREATE_MATLAB_COMMANDS
	createMatlabCommands();
#endif

	std::cout << "Press any button to quit." <<std::endl;
	getchar();
}